﻿using System.Collections.ObjectModel;
using System;
using System.Data.SqlServerCe;
using System.IO;
using NHibernate.Cfg;
using NHibernate.Tool.hbm2ddl;
using NHibernate;
using System.Reflection;
using System.ComponentModel;

namespace SchoolDomain
{
    /// <summary>
    /// Класс-фабрика оценок.
    /// </summary>
    public class MarkFactory
    {
        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        private static MarkFactory instance;

        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        public static MarkFactory Instance
        {
            get
            {
                if (instance == null)
                    instance = new MarkFactory();
                return instance;
            }
        }

        /// <summary>
        /// Коллекция оценок.
        /// </summary>
        private ObservableCollection<Mark> marks;

        /// <summary>
        /// Колеекция оценок.
        /// </summary>
        public ObservableCollection<Mark> Marks
        {
            get { return marks; }
        }

        /// <summary>
        /// Создать фабрику оценок.
        /// </summary>
        private MarkFactory()
        {
            marks = new ObservableCollection<Mark>();
        }

        /// <summary>
        /// Найти индекс оценки в коллекции.
        /// </summary>
        /// <param name="student">ученик</param>
        /// <param name="unit">урок</param>
        /// <returns>индекс оценки в коллекции (если есть)</returns>
        private int FindMark(Student student, Unit unit)
        {
            int markIndex = 0;
            while (markIndex < marks.Count)
            {
                if ((marks[markIndex].Student == student) && (marks[markIndex].Unit == unit))
                    break;
                markIndex++;
            }
            if (markIndex < marks.Count)
                return markIndex;
            else
                return -1;
        }

        /// <summary>
        /// Создать пустую (неотображаемую) оценку или вернуть существующую.
        /// </summary>
        /// <param name="student">ученик</param>
        /// <param name="unit">урок</param>
        public Mark GetMark(Student student, Unit unit)
        {
            int markIndex = FindMark(student, unit);
            if (markIndex > 0)
                return marks[markIndex];
            else
            {
                Mark result = new Mark
                {
                    StandardMark = null,
                    Absence = false,
                    BeingLate = false,
                    CreativeActivity = false,
                    DestructiveConflict = false,
                    DisciplineNote = false,
                    Student = student,
                    Unit = unit
                };
                marks.Add(result);
                return result;
            }
        }

        /// <summary>
        /// Поставить указанную оценку ученику за указанный урок.
        /// </summary>
        /// <param name="mark">оценка</param>
        public void SetMark(Mark mark)
        {
            int markIndex = FindMark(mark.Student, mark.Unit);
            if (markIndex > 0)
                marks[markIndex] = mark;
            else
                marks.Add(mark);
        }

        /// <summary>
        /// Считать все оценки из текущей сессии.
        /// </summary>
        public void Load()
        {
            foreach (Mark mark in Domain.Instance.Session.CreateCriteria<Mark>().List<Mark>())
                SetMark(mark);
        }

        /// <summary>
        /// Сохранить все оценки в текущей сессии.
        /// </summary>
        public void Save()
        {
            foreach (Mark mark in MarkFactory.Instance.Marks)
                if (mark.IsEmpty())
                    Domain.Instance.Session.Delete(mark);
                else
                    Domain.Instance.Session.SaveOrUpdate(mark);
        }

        /// <summary>
        /// Удалить все оценки.
        /// </summary>
        public void Clear()
        {
            foreach (Mark mark in marks)
                Domain.Instance.Session.Delete(mark);
            marks.Clear();
        }
    }

    /// <summary>
    /// Класс-фабрика учеников.
    /// </summary>
    public class StudentFactory
    {
        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        private static StudentFactory instance;

        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        public static StudentFactory Instance
        {
            get
            {
                if (instance == null)
                    instance = new StudentFactory();
                return instance;
            }
        }

        /// <summary>
        /// Коллекция оценок.
        /// </summary>
        private ObservableCollection<Student> students;

        /// <summary>
        /// Коллекция учеников.
        /// </summary>
        public ObservableCollection<Student> Students
        {
            get { return students; }
        }

        /// <summary>
        /// Создать фабрику учеников.
        /// </summary>
        private StudentFactory()
        {
            students = new ObservableCollection<Student>();
        }

        /// <summary>
        /// Получить ученика, отображаемого пустой строкой.
        /// </summary>
        /// <returns>ученик</returns>
        public Student GetEmptyStudent()
        {
            Student result = new Student { Name = "" };
            students.Add(result);
            return result;
        }

        /// <summary>
        /// Загрузить учеников из текущей сессии.
        /// </summary>
        public void Load()
        {
            students.Clear();
            foreach (Student student in Domain.Instance.Session.CreateCriteria<Student>().List<Student>())
                students.Add(student);
        }

        /// <summary>
        /// Сохранить всех учеников в текущей сессии.
        /// </summary>
        public void Save()
        {
            foreach (Student student in students)
                if (student.Name == "")
                    Domain.Instance.Session.Delete(student);
                else
                    Domain.Instance.Session.SaveOrUpdate(student);
        }

        /// <summary>
        /// Удалить всех учеников.
        /// </summary>
        public void Clear()
        {
            foreach (Student student in StudentFactory.Instance.Students)
                Domain.Instance.Session.Delete(student);
            students.Clear();
        }
    }

    /// <summary>
    /// Класс-фабрика уроков.
    /// </summary>
    public class UnitFactory
    {
        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        private static UnitFactory instance;

        /// <summary>
        /// Экземпляр фабрики.
        /// </summary>
        public static UnitFactory Instance
        {
            get
            {
                if (instance == null)
                    instance = new UnitFactory();
                return instance;
            }
        }

        /// <summary>
        /// Коллекция уроков.
        /// </summary>
        private ObservableCollection<Unit> units;

        /// <summary>
        /// Колеекция уроков.
        /// </summary>
        public ObservableCollection<Unit> Units
        {
            get { return units; }
        }

        /// <summary>
        /// Создать фабрику уроков.
        /// </summary>
        private UnitFactory()
        {
            units = new ObservableCollection<Unit>();
        }

        /// <summary>
        /// Создать урок за сегодняшнюю дату.
        /// </summary>
        /// <returns></returns>
        public Unit GetNewUnit()
        {
            Unit result = new Unit { UnitDate = DateTime.Now };
            units.Add(result);
            return result;
        }

        /// <summary>
        /// Удалить переданный объект урока.
        /// </summary>
        /// <param name="unit">урок</param>
        public void Delete(Unit unit)
        {
            units.Remove(unit);
            Domain.Instance.Session.Delete(unit);
        }

        /// <summary>
        /// Загрузить уроки из текущей сессии.
        /// </summary>
        public void Load()
        {
            units.Clear();
            foreach (Unit unit in Domain.Instance.Session.CreateCriteria<Unit>().List<Unit>())
                units.Add(unit);
        }

        /// <summary>
        /// Сохранить все уроки в текущей сессии.
        /// </summary>
        public void Save()
        {
            foreach (Unit unit in units)
                Domain.Instance.Session.SaveOrUpdate(unit);
        }

        /// <summary>
        /// Удалить все уроки.
        /// </summary>
        public void Clear()
        {
            foreach (Unit unit in units)
                Domain.Instance.Session.Delete(unit);
            units.Clear();
        }
    }

    /// <summary>
    /// Класс предметной области.
    /// </summary>
    public class Domain: INotifyPropertyChanged
    {
        #region Поля и методы
        /// <summary>
        /// Экземпляр объекта.
        /// </summary>
        private static Domain instance;
        /// <summary>
        /// Экземпляр объекта.
        /// </summary>
        public static Domain Instance
        {
            get 
            {
                if (instance == null)
                    instance = new Domain();
                return instance;
            }
        }

        /// <summary>
        /// Фабрика сессий.
        /// </summary>
        private ISessionFactory sessionFactory;
        /// <summary>
        /// Фабрика сессий.
        /// </summary>
        public ISessionFactory SessionFactory
        {
            get { return sessionFactory; }
        }

        /// <summary>
        /// Сессия.
        /// </summary>
        private ISession session;
        /// <summary>
        /// Сессия.
        /// </summary>
        public ISession Session
        {
            get { return session; }
        }

        /// <summary>
        /// Имя файла базы данных.
        /// </summary>
        private string databaseName;
        /// <summary>
        /// Имя файла базы данных.
        /// </summary>
        public string DatabaseName
        {
            get { return databaseName; }
            set
            {
                databaseName = value;
                OnPropertyChanged("DatabaseName");
            }
        }
        #endregion

        /// <summary>
        /// Инициализировать предметную область.
        /// </summary>
        /// <param name="databaseName">имя файла базы данных</param>
        public void Load(string databaseName)
        {
            Clear();
            if (session != null)
                session.Close();
            DatabaseName = databaseName;
            string connectionString = string.Format("Data Source={0}", databaseName);
            Configuration configuration = new Configuration();
            configuration.Configure();
            configuration.Properties[NHibernate.Cfg.Environment.ConnectionString] = connectionString;
            configuration.AddAssembly(Assembly.GetExecutingAssembly());
            if (!File.Exists(databaseName))
            {
                SqlCeEngine engine = new SqlCeEngine(connectionString);
                engine.CreateDatabase();
                new SchemaExport(configuration).Execute(false, true, false);
            }
            sessionFactory = configuration.BuildSessionFactory();
            session = sessionFactory.OpenSession();
            StudentFactory.Instance.Load();
            UnitFactory.Instance.Load();
            MarkFactory.Instance.Load();
        }

        /// <summary>
        /// Сохранить предметную область.
        /// </summary>
        public void Save()
        {
            using (ITransaction transaction = session.BeginTransaction())
            {
                StudentFactory.Instance.Save();
                UnitFactory.Instance.Save();
                MarkFactory.Instance.Save();
                transaction.Commit();
            }
        }

        /// <summary>
        /// Сохранить предметную область в указанный файл базы данных.
        /// </summary>
        /// <param name="databaseName">имя файла базы данных</param>
        public void SaveAs(string databaseName)
        {
            Save();
            File.Copy(this.databaseName, databaseName);
            Load(databaseName);
        }

        /// <summary>
        /// Очистить все данные предметной области.
        /// </summary>
        public void Clear()
        {
            StudentFactory.Instance.Clear();
            UnitFactory.Instance.Clear();
            MarkFactory.Instance.Clear();
        }

        #region INotifyPropertyChanged Members

        /// <summary>
        /// Событие на изменение значения свойства.
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Генерация события на изменение значения свойства.
        /// </summary>
        /// <param name="propertyName">имя свойства</param>
        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #endregion
    }
}